package com.cc.bsp.persistence.hibernate;

import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.metadata.ClassMetadata;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.util.Assert;

import com.cc.bsp.persistence.IOrder;
import com.cc.bsp.persistence.IPage;
import com.cc.bsp.persistence.IQueryPageOrder;
import com.cc.bsp.persistence.entity.IEntity;
import com.cc.bsp.persistence.exception.PersistenceException;
import com.cc.bsp.persistence.hibernate.expand.namedql.template.StatementTemplate;


public abstract class AbstractEntityDao<T extends IEntity,ID extends Serializable> extends HibernateDaoSupport implements IHEntityDao<T, ID>{
	
	public StatementTemplate statementTemplate;
	
	protected Class<T> entityClass;  
	
	public AbstractEntityDao(Class<T> clazzz) {
		this.entityClass = clazzz;
	}
	
	public StatementTemplate getStatementTemplate() {
		return statementTemplate;
	}

	public void setStatementTemplate(StatementTemplate statementTemplate) {
		this.statementTemplate = statementTemplate;
	}

	public Class<T> getEntityClass(){
		return entityClass;
	}
	
	protected void setEntityClass(Class<T> entityClass) {
		this.entityClass = entityClass;
	}

	public Criteria createCriteria(final Criterion... criterions) {
		
		Assert.noNullElements(criterions, "criterions is null");
		
		Criteria criteria = getSession().createCriteria(entityClass);
		
		for (Criterion c : criterions) {
			criteria.add(c);
		}
		return criteria;
	}


	public void save(T entity) throws PersistenceException {
	
		Assert.notNull(entity);
		
		getHibernateTemplate().save(entity);
	}

	public void update(T entity) throws PersistenceException {
		
		Assert.notNull(entity);
		
		getHibernateTemplate().update(entity);
		
	}

	public void deleteById(ID id) throws PersistenceException {
		Assert.notNull(id);
		T  entity = findById(id);
		getHibernateTemplate().delete(entity);
		
	}

	public void delete(T entity) throws PersistenceException {
		Assert.notNull(entity);
		getHibernateTemplate().delete(entity);
		
	}

	public void deleteBatch(List<T> list) throws PersistenceException {
		Assert.notEmpty(list);
		getHibernateTemplate().deleteAll(list);
	}

	public void add(T entity) throws PersistenceException {
		Assert.notNull(entity);
		getHibernateTemplate().save(entity);
	}

	public void addBatch(List<T> list) throws PersistenceException {
		Assert.notEmpty(list);
		for(T entity:list){
			getHibernateTemplate().save(entity);
		}
	}

	public T findById(ID id) throws PersistenceException {
		Assert.notNull(id);
		T t = getHibernateTemplate().load(getEntityClass(), id);
		return t;
	}

	@SuppressWarnings("unchecked")
	public T findUniqueBy(String propertyName, Object propertyValue)
			throws PersistenceException {
		
		Assert.hasLength(propertyName);
		Assert.notNull(propertyValue);
		
		 Criterion criterion = Restrictions.eq(propertyName, propertyValue);  
		
		 return ((T) createCriteria(criterion).uniqueResult()); 
	}

	@SuppressWarnings("unchecked")
	public List<T> get(Collection<ID> ids) throws PersistenceException {
		 Assert.notEmpty(ids);
		 ClassMetadata meta = getSessionFactory().getClassMetadata(entityClass);  

		 String id  = meta.getIdentifierPropertyName();  

		 Criterion criterions = Restrictions.in(id, ids);
		 
		 Criteria criteria = createCriteria(criterions);
				 
		return criteria.list();
	}

	@SuppressWarnings("unchecked")
	public List<T> get(Collection<ID> ids, IQueryPageOrder pageorder)
			throws PersistenceException {
		 Assert.notEmpty(ids);
		 Assert.notNull(pageorder);
		 ClassMetadata meta = getSessionFactory().getClassMetadata(entityClass);  

		 String id  = meta.getIdentifierPropertyName();  

		 Criterion criterions = Restrictions.in(id, ids);  
		 
		 Criteria countCriteria = createCriteria(criterions);
		 
		 long totalElements = ((Long) countCriteria.setProjection(Projections.rowCount()).uniqueResult()).intValue();
		 
		 pageorder.createPage(totalElements);
		 
		 
		 Criteria criteria = createCriteria(pageorder);

		 criteria.add(criterions);
	
		 List<T> list = createCriteria(criterions).list();  
		 

		return list;
	}

	@SuppressWarnings("unchecked")
	public List<T> findList(String propertyName, Object propertyValue)
			throws PersistenceException {
		Assert.hasLength(propertyName);
		Assert.notNull(propertyValue);
		Criterion criterion = Restrictions.eq(propertyName, propertyValue);  
		
		 return createCriteria(criterion).list(); 
	}

	@SuppressWarnings("unchecked")
	public List<T> findList(String propertyName, Object propertyValue,
			IQueryPageOrder pageorder) throws PersistenceException {
		Assert.hasLength(propertyName);
		Assert.notNull(propertyValue);
		Assert.notNull(pageorder);
		
		String hql = " from " + getEntityClass().getSimpleName() + " where " + propertyName + " = :"+propertyName;
		
		Map<String,Object> map = new HashMap<String,Object>();
		
		map.put(propertyName, propertyValue);
		
		pageorder.createPage(getCount(hql, map));

		Query query = createQueryXQLQuery(hql,IHRootDao.TYPE.HQL,pageorder);
		
		query.setProperties(map);
		
		return query.list();
	}

	@SuppressWarnings("unchecked")
	public List<T> findAll() throws PersistenceException {
		
		String hql = " from " + getEntityClass().getSimpleName();
		
		Query query = getSession().createQuery(hql);
		
		return query.list();
	}

	@SuppressWarnings("unchecked")
	public List<T> findAll(IQueryPageOrder pageorder) throws PersistenceException {
		Assert.notNull(pageorder);
		Criteria c = createCriteria(pageorder);
	
		return c.list();
	}

	
	public long getCount(Criteria c) throws PersistenceException  {
		Assert.notNull(c);
		long total = ((Long) c.setProjection(Projections.rowCount()).uniqueResult()).intValue();

		return total;

	}
	
	public long getCount(String hql, T entity) throws PersistenceException {
		Assert.hasLength(hql);
		Assert.notNull(entity);
		
		String countHSQL = "select  count(*) " + hql + " ";

		Query query = getSession().createQuery(countHSQL);
	
		query.setProperties(entity);

		long total = ((Long) query.iterate().next()).intValue();

		return total;
	}
	

	public long getCount(final String hql,final Map<String,Object> map) throws PersistenceException {
		Assert.hasLength(hql);
		Assert.notEmpty(map);
		
		
		String countHSQL = "select  count(*) " + hql + " ";

		Query query = getSession().createQuery(countHSQL);
	
		query.setProperties(map);

		long total = ((Long) query.iterate().next()).intValue();

		return total;
	}
	
	public long getCount(String hql) throws PersistenceException {
		Assert.hasLength(hql);
	
		String countHSQL = "select  count(*) " + hql + " ";

		Query query = getSession().createQuery(countHSQL);
	
		long total = ((Long) query.iterate().next()).intValue();

		return total;
	}
	
	@SuppressWarnings("unchecked")
	public List<T> findList(Criterion... criterions) throws PersistenceException {
		Assert.noNullElements(criterions);
		
		List<T> list = createCriteria(criterions).list();  
		
		return list;
	}

	@SuppressWarnings("unchecked")
	public List<IEntity> findList(String hql, T entity,IQueryPageOrder pageorder) throws PersistenceException {
		Assert.hasLength(hql);
		Assert.notNull(entity);
		Assert.notNull(pageorder);
		pageorder.createPage(getCount(hql, entity));
		
		Query query = createQueryXQLQuery(hql,IHRootDao.TYPE.HQL,pageorder);
		
		query.setProperties(entity);

		List<IEntity> list = query.list();
		
		return list;
	}

	
	protected Query createQueryXQLQuery(String queryString,IHRootDao.TYPE type,IQueryPageOrder pageorder) {
		Assert.hasLength(queryString);
		Assert.notNull(type);
		Assert.notNull(pageorder);
		IOrder order = null;
		
		IPage page = null;

		String finalQueryString = null;

		if (pageorder != null) {
			order = pageorder.getOrder();
			page = pageorder.getPage();
		}

		if (order != null) {
			String orderSQL = order.getOrderSQL();
			finalQueryString = queryString + " " + orderSQL;
		} else {
			finalQueryString = queryString;
		}

		Query query = null;
		
		if (IHRootDao.TYPE.HQL == type) {
			query = getSession().createQuery(finalQueryString);
		} else if (IHRootDao.TYPE.SQL == type) {
			query = getSession().createSQLQuery(finalQueryString);
		} else {
			return null;
		}

		if (page != null) {
			query.setFirstResult(page.getCurrentBeginRow());
			query.setMaxResults(page.getCurrentEndRow());
		}
		return query;
	}
	
	protected Criteria createCriteria(IQueryPageOrder pageorder) {
		
		Assert.notNull(pageorder);
		
		Criteria countcriteria = getSession().createCriteria(entityClass);
		
		long totalElements = ((Long) countcriteria.setProjection(Projections.rowCount()).uniqueResult()).intValue();
		 
		pageorder.createPage(totalElements);
			
		
		Criteria criteria = getSession().createCriteria(entityClass);
		
		IOrder order = null;
		
		IPage page = null;
		
		if (pageorder != null) {
			order = pageorder.getOrder();
			page = pageorder.getPage();
		}
		
		if (order != null) {
			Map<String,String> map = order.getOrderProperties();
			Iterator<String> i= map.keySet().iterator();
			while (i.hasNext()) {
				String key = i.next();
				String property = map.get(key);

				if (order.getType() == IOrder.TYPE.ASC) {
					criteria.addOrder(Order.asc(property));
				} else {
					criteria.addOrder(Order.desc(property));
				}
			}
		} 

		if (page != null) {
			criteria.setFirstResult(page.getCurrentBeginRow());
			criteria.setMaxResults(page.getCurrentEndRow());
		}
		return criteria;
	}
}
